#include <fstream>
#include <string>
#include <algorithm>
#include <sstream>
#include <cassert>
#include <vector>
#include <list>
#include "solid_loader.h"
#include "matrix.h"
#include "material.h"
#include "triangle.h"
#include "color.h"
#include "obj_parser.h"
#include "studio.h"
namespace gemren{
	using namespace std;

	
	bool solid_loader::load_def(
		studio& stud, 
		std::list<std::string>& configs, 
		std::vector<solid_data>& solids, 
		const std::string& filename, 
		const std::map<std::string,
		std::string>& groupmat, 
		const matrix4x4& transmat, 
		const material_ptr mat
		)
	{
		ifstream f(filename);
		return f.good() && load_def(stud, configs, solids,f, groupmat, transmat, mat); 
	}
	
	bool solid_loader::load_def(
		studio& stud, 
		std::list<std::string>& configs, 
		std::vector<solid_data>& solids, 
		istream& f, const std::map<std::string,
		std::string>& parent_groupmat, 
		const matrix4x4& transmat,
		const material_ptr mat
		)
	{ //TODO pass liquid generators to solids?
		std::map<std::string,std::string> groupmat(parent_groupmat);
		string line,atom;
		matrix4x4 trmat = matrix4x4::identity();
		material_ptr nmat = mat;
		string matname;
		float_type x,y,z; //for transformations
		
		while(! f.eof())
		{
			getline(f,line);
			stringstream ls(line);
			atom = "";
			ls>>atom;
			if(atom == "mat")
			{
				ls>>atom;
				nmat = material::create_material(atom.c_str());
				matname = atom.erase(atom.find_first_of('.'));
				stud.thescene->store_material(matname, nmat);
			}
			else if(atom == "mov")
			{
				ls>>atom;
				x=y=z=0;
				for(string::iterator c = atom.begin(); c!=atom.end(); ++c)
					switch(*c)
					{
					case 'x': ls>>x;
						break;
					case 'y': ls>>y;
						break;
					case 'z': ls>>z;
						break;
					}
				trmat = matrix4x4::move(x,y,z) * trmat;
			}
			else if(atom == "scl")
			{
				ls>>atom;
				x=y=z=1;
				for(string::iterator c = atom.begin(); c!=atom.end(); ++c)
					switch(*c)
					{
					case 'x': ls>>x;
						break;
					case 'y': ls>>y;
						break;
					case 'z': ls>>z;
						break;
					}
				trmat = matrix4x4::scale(x,y,z) * trmat;
			}
			else if(atom == "rot")
			{
				ls>>atom;
				x=0;
				for(string::iterator c = atom.begin(); c!=atom.end(); ++c)
					switch(*c)
					{
					case 'x': ls>>x;
						trmat = matrix4x4::rotateX(x*3.141592654f/180.0f) * trmat;
						break;
					case 'y': ls>>x;
						trmat = matrix4x4::rotateY(x*3.141592654f/180.0f) * trmat;
						break;
					case 'z': ls>>x;
						trmat = matrix4x4::rotateZ(x*3.141592654f/180.0f) * trmat;
						break;
					}
			}
			else if(atom == "[")
			{
				load_def(stud, configs, solids, f, groupmat, transmat*trmat, nmat);
			}
			else if(atom == "]")
			{
				return true;
			}
			else if(atom == "obj")
			{
				ls>>atom;
				solids.push_back(solid_data(transmat * trmat, nmat, atom, groupmat, matname));
			}
			else if(atom == "gmt")
			{
				string matname;
				ls>>atom>>matname;
				groupmat[atom] = matname;
			}
			else if(atom == "sld")
			{
				ls>>atom;
				load_def(stud, configs, solids, atom.c_str(), groupmat, transmat * trmat, nmat);
			}
			else if(atom == "set")
			{
				configs.push_back(line);
			}
		
		}
		return true;
	}

	studio solid_loader::load_scene(const std::string& filename)
	{
		 

		studio stud(new scene);
		stud.cam = new gemren::camera(stud.thescene);
		stud.setdim(640, 480);
		std::vector<solid_data> solids;
		std::list<std::string> configs; // for set prop commands
		size_t extbeg = filename.find_last_of('.'); 
		
		
		if(extbeg != string::npos && filename.substr(extbeg) == ".obj")
		{ //loading bare obj - there wil be no lights -> image will be black
			material_ptr defmat = material::default_material();
			stud.thescene->store_material("default", defmat);
			solids.push_back( solid_data(matrix4x4::identity(), defmat, filename, map<string,string>()));
		}
		else
		{
			if(!load_def(stud, configs, solids, filename, map<string,string>()))
			{
				stud.clean();
				throw 42;
			}
		}
		if(solids.empty()) throw 43;
		ensure_scheduler_init();
		embree::scheduler->addTask(load_triangles, (void*) &solids, solids.size());
		embree::scheduler->go();
		 
		for(std::vector<solid_data>::iterator i = solids.begin(); i!= solids.end(); ++i)
		{
			for(std::map<std::string, material_ptr>::iterator j = i->materials.begin(); j != i->materials.end(); ++j)
				stud.thescene->store_material(j->first, j->second);
			assert(i->mat);
			stud.thescene->add_geometry(i->tris, i->verts, i->norms, i->texs);
		}
		
		stud.thescene->build_accel();
		for(std::list<std::string>::iterator comm = configs.begin(); comm != configs.end(); ++comm)
		{
			stud.set_prop(std::stringstream(*comm));
		}

		return stud;
	 
	}
}